package com.sunday.authorization.security.authorization.redis.deserializer;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.util.Assert;

import java.io.IOException;
import java.util.Map;

/**
 * @see org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationRequestDeserializer
 */
@Slf4j
public class OAuth2AuthorizationDeserializer extends JsonDeserializer<OAuth2Authorization> {

    private RegisteredClientRepository registeredClientRepository;

    public OAuth2AuthorizationDeserializer(RegisteredClientRepository registeredClientRepository) {
        this.registeredClientRepository = registeredClientRepository;
    }

    @Override
    public OAuth2Authorization deserialize(JsonParser parser, DeserializationContext context)
            throws IOException {
        ObjectMapper mapper = (ObjectMapper) parser.getCodec();
        JsonNode root = mapper.readTree(parser);
        return deserialize(parser, mapper, root);
    }

    private OAuth2Authorization deserialize(JsonParser parser, ObjectMapper mapper, JsonNode root) {

        RegisteredClient registeredClient = registeredClientRepository.findById(JsonNodeUtils.findStringValue(root, "registeredClientId"));
        Assert.notNull(registeredClient, "Registered client must not be null");

        OAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient)
                .id(JsonNodeUtils.findStringValue(root, "id"))
                .principalName(JsonNodeUtils.findStringValue(root, "principalName"))
                .authorizationGrantType(JsonNodeUtils.findValue(root, "authorizationGrantType", new TypeReference<>() {
                }, mapper))
                .authorizedScopes(JsonNodeUtils.findValue(root, "scopes", JsonNodeUtils.STRING_SET, mapper))
                .attributes(map -> map.putAll(JsonNodeUtils.findValue(root, "attributes", JsonNodeUtils.STRING_OBJECT_MAP, mapper)));

        Map<Class<? extends OAuth2Token>, OAuth2Authorization.Token<?>> tokens = JsonNodeUtils.findValue(root, "tokens", new TypeReference<>() {
        }, mapper);
        for (Map.Entry<Class<? extends OAuth2Token>, OAuth2Authorization.Token<?>> entry : tokens.entrySet()) {
            OAuth2Authorization.Token token = entry.getValue();
            if (log.isDebugEnabled()) {
                log.debug("token: {}, metadata: {}", token.getToken(), token.getMetadata());
            }
            builder.token(token.getToken(), map -> map.putAll(token.getMetadata()));
        }
        return builder.build();
    }

}
